﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Xml.Linq;

using ApocDev.TownsApi.Aspects;

namespace ApocDev.TownsApi.XmlFiles
{
    [NotifyPropertyChanged]
    public class ZonesXml : ObservableCollection<Zone>, IChangedXml
    {
        // Unfortunately, this is one of those files where things have to be "specially" handled to allow
        // people to create their own things.
        public ZonesXml(string actionsPath)
        {
            ModFiles.Zones = this;
            XElement root = XElement.Load(actionsPath);
            IEnumerable<XElement> elements = root.Elements();
            int total = elements.Count();
            ModFiles.FireLoadEvent(this, new FileLoadEventArgs("Zones", total, 0));
            int numLoaded = 0;
            foreach (XElement xml in elements)
            {
                Add(new Zone(xml));
                ModFiles.FireLoadEvent(this, new FileLoadEventArgs("Zones", total, ++numLoaded));
            }
        }

        public bool IsDirty { get { return this.Any(a => a.IsDirty); } }

        public XElement SaveDirty()
        {
            if (!IsDirty)
                return null;
            var actions = new XElement("zones");
            foreach (Zone a in this)
            {
                if (a.IsDirty)
                    actions.Add(a.Save());
            }
            return actions;
        }

        public XElement Save()
        {
            var actions = new XElement("zones");
            foreach (Zone a in this)
            {
                actions.Add(a.Save());
            }
            return actions;
        }
    }

    [NotifyPropertyChanged]
    public class Zone : IChangedXml, IEquatable<Zone>
    {
        private readonly int _originalHash;

        public Zone(XElement xml)
        {
            ID = xml.Name.LocalName;
            foreach (XElement e in xml.Elements())
            {
                switch (e.Name.LocalName.ToLower())
                {
                    case "name":
                        Name = e.Value;
                        break;
                    case "type":
                        Type = e.Value;
                        break;
                    case "minwidth":
                        MinWidth = int.Parse(e.Value);
                        break;
                    case "minheight":
                        MinHeight = int.Parse(e.Value);
                        break;
                    case "neighbor":
                        Neighbor = ModFiles.GetZone(e.Value);
                        break;
                    default:
                        MessageBox.Show("Unknown element for " + GetType().Name + ": " + e.Name.LocalName);
                        break;
                }
            }
            _originalHash = GetHashCode();
        }

        public string ID { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public int MinWidth { get; set; }
        public int MinHeight { get; set; }
        public Zone Neighbor { get; set; }
        public bool IsDirty { get { return _originalHash != GetHashCode(); } }

        public XElement Save()
        {
            return new XElement(ID,
                new XElement("name", Name),
                new XElement("type", Type),
                Xml.AddIf("minWidth", MinWidth),
                Xml.AddIf("minHeight", MinHeight),
                Xml.AddIf("neighbor", () => Neighbor.ID, Neighbor != null));
        }

        public XElement SaveDirty()
        {
            return Save();
        }

        public bool Equals(Zone other)
        {
            if (ReferenceEquals(null, other))
                return false;
            if (ReferenceEquals(this, other))
                return true;
            return string.Equals(ID, other.ID) && string.Equals(Name, other.Name) && string.Equals(Type, other.Type) &&
                   MinWidth == other.MinWidth && Equals(Neighbor, other.Neighbor) && MinHeight == other.MinHeight;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            if (obj.GetType() != GetType())
                return false;
            return Equals((Zone) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hashCode = (ID != null ? ID.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (Type != null ? Type.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ MinWidth;
                hashCode = (hashCode * 397) ^ (Neighbor != null ? Neighbor.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ MinHeight;
                return hashCode;
            }
        }

        public static bool operator ==(Zone left, Zone right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(Zone left, Zone right)
        {
            return !Equals(left, right);
        }
    }
}